﻿#region Usings

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using Ninject;
using Rail.Net.Common.Model;
using Rail.Net.Core.Interfaces.Data;
using Rail.Net.Core.Interfaces.Services;

#endregion

namespace Rail.Net.Services
{
    public class StationLookupService : IStationLookupService
    {
        //private const string Url = "89.110.144.87"; // mario

        //private const string Url = "94.249.153.7"; // markus

        private const string Url = "fahrplan.pv.oebb.at"; // fahrplan

        //private const Int32 Port = 5555;    // mario

        //private const Int32 Port = 43210;    // markus

        private const Int32 Port = 8880; // fahrplan

        [Inject]
        public IDalContext DalContext { get; set; }

        #region IStationLookupService Members

        public List<NameValuePair> GetStations(string fromStation, string toStation, bool forwardDirection)
        {
            // get the Hafas stations
            var serverOutput = GetServerResponse(fromStation, toStation);
            var fromStations = new List<NameValuePair>();
            var toStations = new List<NameValuePair>();
            ParseOutput(serverOutput, fromStations, toStations);
            var result = forwardDirection ? fromStations : toStations;


            // get the DB stations
            var searchTerm = forwardDirection ? fromStation : toStation;
            if (!string.IsNullOrEmpty(searchTerm))
            {
                try
                {
                    var evas = result.Select(n => n.Value);
                    if (searchTerm.Length < 4)
                    {
                        result = result.Union(DalContext.StationRepository.AllDefaultsIncluded().Where(
                            s => !evas.Contains(s.Eva) && s.Name.ToLower().StartsWith(searchTerm.ToLower())).
                                                  Select(s => new NameValuePair
                                                                  {
                                                                      Name = s.Name,
                                                                      Value = s.Eva
                                                                  })).OrderBy(n => n.Name).ToList();
                    }
                    else
                    {
                        result = result.Union(DalContext.StationRepository.AllDefaultsIncluded().Where(
                            s => !evas.Contains(s.Eva) && s.Name.ToLower().Contains(searchTerm.ToLower())).
                                                  Select(s => new NameValuePair
                                                                  {
                                                                      Name = s.Name,
                                                                      Value = s.Eva
                                                                  })).OrderBy(n => n.Name).ToList();
                    }
                }
                catch (Exception e)
                {
                    var a = 9;
                }
            }

            return result;
        }

        #endregion

        private static string GetServerResponse(string from, string to)
        {
            try
            {
                var buffer = new byte[4096];

                // create the TCP Client to the given url and port
                var client = new TcpClient(Url, Port);

                // create the string for the TCP communication between the client and the server
                var stream = client.GetStream();

                // read the first 4 irrelevant bytes
                stream.Read(buffer, 0, 4);


                // read the next 12 bytes containing the number of remaining bytes to be read
                stream.Read(buffer, 0, 12);

                // skip the bytes that are not ASCII numbers
                var newBuff = buffer.TakeWhile(b => b >= 48 && b <= 57).ToArray();

                // convert the number to string
                var responseData = Encoding.ASCII.GetString(newBuff, 0, newBuff.Length);

                // convert it to integer
                var size = Int32.Parse(responseData);

                // read the remaining content from the stream
                stream.Read(buffer, 0, size);

                // get the content into string
                Encoding.ASCII.GetString(buffer, 0, size);

                // create the query
                var query = String.Format("BF&A=0.0.0.0&S={0}&Z={1}&", from, to);
                //var query = "BF&A=0.0.0.0&S=" + "Wien" + "&Z=" + "Munchen" + "&";

                // convert the query to byte array
                var queryBytes = Encoding.ASCII.GetBytes(query.Length.ToString("000000000000") + query);

                // send it to the server by writing to the stream
                stream.Write(queryBytes, 0, queryBytes.Length);

                // reset the buffer
                Array.Clear(buffer, 0, buffer.Length);

                // read the next 12 bytes containing the number of remaining bytes to be read
                stream.Read(buffer, 0, 12);

                // skip the bytes that are not ASCII numbers
                newBuff = buffer.TakeWhile(b => b >= 48 && b <= 57).ToArray();

                // convert the number to string
                responseData = Encoding.ASCII.GetString(newBuff, 0, newBuff.Length);

                // convert it to integer
                size = Int32.Parse(responseData);

                // read the remaining content from the stream
                stream.Read(buffer, 0, size);

                // get the content into string
                responseData = Encoding.GetEncoding("iso-8859-1").GetString(buffer, 0, size);

                // Close everything.
                stream.Close();
                client.Close();

                return responseData;
            }
            catch (ArgumentNullException e)
            {
                Console.WriteLine("ArgumentNullException: {0}", e);
            }
            catch (SocketException e)
            {
                Console.WriteLine("SocketException: {0}", e);
            }

            return "";
        }

        private static void ParseOutput(string output, ICollection<NameValuePair> fromStations,
                                        ICollection<NameValuePair> toStations)
        {
            var items = output.Split(new[] {'&'}, StringSplitOptions.RemoveEmptyEntries);
            foreach (var item in items)
            {
                var pairs = item.Substring(2).Split(new[] {'#'}, StringSplitOptions.RemoveEmptyEntries);


                // start station
                if (item.ToLower().StartsWith("s="))
                {
                    fromStations.Add(new NameValuePair {Name = pairs[0], Value = pairs[1]});
                }

                // end station
                if (item.ToLower().StartsWith("z="))
                {
                    toStations.Add(new NameValuePair {Name = pairs[0], Value = pairs[1]});
                }
            }
        }
    }
}